home *** CD-ROM | disk | FTP | other *** search
/ AOL File Library: 2,801 to 2,900 / aol-file-protocol-4400-2801-to-2900.zip / AOLDLs / C++ Files Library / CWASTE C++ text editing routi / CWASTE folder.sit / CWASTE folder / WASTE4.c < prev    next >
Text File  |  1994-07-20  |  28KB  |  1,147 lines

  1. //unit WASTE4;
  2.  
  3. // { WASTE PROJECT: }
  4. // { Unit Four: Editing }
  5.  
  6. // { Copyright ⌐ 1993-1994 Marco Piovanelli }
  7. // { All Rights Reserved }
  8. // C conversion by Dan Crevier
  9.  
  10. #include "WASTEIntf.h"
  11.  
  12. OSErr _WEInsertRun(long runIndex, long offset, long styleIndex, WEPtr pWE)
  13. {
  14.     // { Insert a new element in the style run array, at the specified runIndex position. }
  15.     // { The new element consists of the pair <offset, styleIndex>. }
  16.  
  17.     RunArrayElement element;
  18.     OSErr err;
  19.  
  20.     // _WEInsertRun = noErr;
  21.  
  22.     // { prepare the element record to be inserted in the array }
  23.     element.runStart = offset;
  24.     element.styleIndex = styleIndex;
  25.  
  26.     // { do the insertion }
  27.     err = _WEInsertSlot((Handle)pWE->hRuns, (Ptr)&element, runIndex + 1, sizeof(element));
  28.     if (err != noErr) 
  29.     {
  30.         return err;
  31.     }
  32.     
  33.     // { increment style run count }
  34.     pWE->nRuns = pWE->nRuns + 1;
  35.  
  36.     // { increment the reference count field of the style table element }
  37.     // { referenced by the newly inserted style run }
  38.     (*pWE->hStyles)[styleIndex].refCount = (*pWE->hStyles)[styleIndex].refCount + 1;
  39.     
  40.     return noErr;
  41. }
  42.  
  43. OSErr _WERemoveRun(long runIndex, WEPtr pWE)
  44. {
  45.     // { remove the specified element from the style run array }
  46.     long styleIndex;
  47.     OSErr err;
  48.     
  49.     styleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  50.  
  51.     // { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  52.     err = _WERemoveSlot((Handle)pWE->hRuns, runIndex, sizeof(RunArrayElement));
  53.  
  54.     // { decrement style run count }
  55.     pWE->nRuns = pWE->nRuns - 1;
  56.  
  57.     // { decrement the reference count field of the style table element }
  58.     // { that was referenced by the style run we have just removed }
  59.     (*pWE->hStyles)[styleIndex].refCount = (*pWE->hStyles)[styleIndex].refCount - 1;
  60.  
  61.     return err;
  62. }
  63.  
  64. void _WEChangeRun(long runIndex, long newStyleIndex, WEPtr pWE)
  65. {
  66.     // { change the styleIndex field of the specified element of the style run array }
  67.  
  68.     long oldStyleIndex;
  69.     
  70.     // { do the change }
  71.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  72.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  73.     
  74.     // { decrement the reference count field of the old style table element }
  75.     (*pWE->hStyles)[oldStyleIndex].refCount = (*pWE->hStyles)[oldStyleIndex].refCount - 1;
  76.  
  77.     // { increment the reference count field of the new style table element }
  78.     (*pWE->hStyles)[newStyleIndex].refCount = (*pWE->hStyles)[newStyleIndex].refCount + 1;
  79. }
  80.  
  81. OSErr _WENewStyle(WERunAttributes *ts, long *styleIndex, WEPtr pWE)
  82. {
  83.     // { given the specified WERunAttributes record, find the corresponding entry }
  84.     // { in the style table (create a new entry if necessary), and return its index }
  85.  
  86.     StyleTablePtr pTable;
  87.     StyleTableElement element;
  88.     long index, unusedIndex;
  89.     OSErr err;
  90.  
  91.     // _WENewStyle = noErr;
  92.     pTable = *pWE->hStyles;
  93.  
  94.     // { see if the given style already exists in the style table }
  95.     // { while scanning the table, also remember the position of the first unused style, if any }
  96.     index = 0;
  97.     unusedIndex = -1;
  98.     while (index < pWE->nStyles)
  99.     {
  100.         // { perform a bitwise comparison between the current element and the specified style }
  101.         if (_WEBlockCmp((Ptr)&pTable[index].info, (Ptr)ts, sizeof(WERunAttributes)))
  102.         { 
  103.             *styleIndex = index;        // { found: style already present }
  104.             return noErr;
  105.         }
  106.  
  107.         // { check for entries which aren't referenced and can be recycled }
  108.         if (pTable[index].refCount == 0) 
  109.         {
  110.             unusedIndex = index;
  111.         }
  112.         index = index + 1;
  113.     } // { while }
  114.  
  115.     // { the specified style doesn't exist in the style table }
  116.     // { see if we can recycle an unused entry }
  117.     if (unusedIndex >= 0) 
  118.     {
  119.         index = unusedIndex;
  120.         pTable[index].info = *ts;
  121.     }
  122.     else
  123.     {
  124.         // { no reusable entry: we have to append a new element to the table }
  125.         element.refCount = 0;
  126.         element.info = *ts;
  127.         err = _WEInsertSlot((Handle)pWE->hStyles, (Ptr)&element, index, sizeof(element));
  128.         if (err != noErr) 
  129.         {
  130.             return err;
  131.         }
  132.  
  133.         // { update style count in the WE record }
  134.         pWE->nStyles = index + 1;
  135.     }
  136.  
  137.     // { return the index to the new element }
  138.     *styleIndex = index;
  139.  
  140.     return noErr;
  141. }
  142.  
  143. OSErr _WERedraw(long rangeStart, long rangeEnd, WEHandle hWE)
  144. {
  145.     // { the WE record is guaranteed to be already locked }
  146.     WEPtr pWE;
  147.     LineArrayPtr pLines;
  148.     long startLine, endLine;
  149.     long oldTextHeight, newTextHeight;
  150.     LongRect r;
  151.     Rect viewRect, updateRect;
  152.     RgnHandle saveClip;
  153.     GrafPtr savePort;
  154.     OSErr err;
  155.     
  156.     pWE = *hWE;
  157.  
  158.     // { do nothing if recalculation has been inhibited }
  159.     if (!BTST(pWE->flags, weFInhibitRecal)) 
  160.     {
  161.         // { hide the caret }
  162.         if (BTST(pWE->flags, weFCaretVisible))
  163.         { 
  164.             _WEDrawCaret(hWE);
  165.         }
  166.         
  167.         // { remember total text height }
  168.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  169.  
  170.         // { find line range affected by modification }
  171.         startLine = _WEOffsetToLine(rangeStart, hWE);
  172.         endLine = _WEOffsetToLine(rangeEnd, hWE);
  173.  
  174.         // { recalculate line breaks starting from startLine }
  175.         err = _WERecalBreaks(&startLine, &endLine, hWE);
  176.         if (err != noErr) 
  177.         {
  178.             goto cleanup;
  179.         }
  180.  
  181.         // { recalculate slops }
  182.         _WERecalSlops(startLine, endLine, hWE);
  183.  
  184.         // { calculate new total text height }
  185.         newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  186.  
  187.         // { calculate the rectangle to redraw (in long coordinates) }
  188.         r.left = -SHRT_MAX;
  189.         r.right = SHRT_MAX;
  190.         pLines = *pWE->hLines;
  191.         r.top = pLines[startLine].lineOrigin;
  192.  
  193.         // { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  194.         // { otherwise we must redraw all lines from startLine on }
  195.         if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1)) 
  196.         {
  197.             r.bottom = pLines[endLine + 1].lineOrigin;
  198.         }
  199.         else if (newTextHeight < oldTextHeight) 
  200.         {
  201.             r.bottom = oldTextHeight;
  202.         }
  203.         else
  204.         {
  205.             r.bottom = newTextHeight;
  206.         }
  207.         
  208.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  209.  
  210.         // { calculate the intersection between this rectangle and the view rectangle }
  211.         WELongRectToRect(&r, &updateRect);
  212.         WELongRectToRect(&pWE->viewRect, &viewRect);
  213.  
  214.         if (SectRect(&updateRect, &viewRect, &updateRect)) 
  215.         {
  216.             // { set up the port and the clip region }
  217.             GetPort(&savePort);
  218.             SetPort(pWE->port);
  219.  
  220.             // { set the clip region to updateRect }
  221.             saveClip = NewRgn();
  222.             GetClip(saveClip);
  223.             ClipRect(&updateRect);
  224.  
  225.             // { we only really need to redraw the visible lines }
  226.             startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  227.             endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  228.  
  229.             // { redraw the lines (pass TRUE in the doErase parameter) }
  230.             _WEDrawLines(startLine, endLine, true, hWE);
  231.  
  232.             // { erase the portion of the update rectangle below the last line (if any) }
  233.             pLines = *pWE->hLines;
  234.             updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  235.             if (updateRect.top < updateRect.bottom) 
  236.             {
  237.                 EraseRect(&updateRect);
  238.             }
  239.  
  240.             // { restore the clip region }
  241.             SetClip(saveClip);
  242.             DisposeRgn(saveClip);
  243.  
  244.             // { restore the port }
  245.             SetPort(savePort);
  246.  
  247.             // { redraw the caret or the selection range }
  248.             if (pWE->selStart < pWE->selEnd) 
  249.             {
  250.                 _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  251.             }
  252.             else
  253.             {
  254.                 _WEDrawCaret(hWE);
  255.             }
  256.         } // { if SectRect }
  257.  
  258.         // { scroll the selection range into view }
  259.         WESelView(hWE);
  260.     } // { if recal not inhibited }
  261.  
  262.     // { clear result code }
  263.     err = noErr;
  264.  
  265. cleanup:
  266.     // { return result code }
  267.     return err;
  268. }
  269.  
  270. OSErr _WESetStyleRange(long rangeStart, long rangeEnd, short mode, WETextStyle *ts, WEHandle hWE)
  271. {
  272.     // { alter the style attributes of the specified text range according to ts and mode }
  273.     // { the WE record is guaranteed to be already locked }
  274.  
  275.     WEPtr pWE;
  276.     RunArrayHandle hRuns;
  277.     long offset;
  278.     long runIndex;
  279.     long oldStyleIndex, newStyleIndex;
  280.     WERunInfo runInfo;
  281.     short temp;
  282.     char continuousStyles;
  283.     OSErr err;
  284.  
  285.     pWE = *hWE;
  286.     hRuns = pWE->hRuns;
  287.  
  288.     // { if mode contains weDoToggleFace, we need to determine which QuickDraw styles }
  289.     // { are continuous over the specified text range: those styles must be turned off }
  290.     if (BTST(mode, kModeToggleFace)) 
  291.     {
  292.         temp = weDoFace;
  293.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  294.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  295.     }
  296.     else
  297.     {
  298.         continuousStyles = 0;
  299.     }
  300.  
  301.     // { find the index to the first style run in the specified range }
  302.     offset = rangeStart;
  303.     runIndex = _WEOffsetToRun(offset, hWE);
  304.  
  305.     // { run thru all the style runs that encompass the selection range }
  306.     do
  307.     {
  308.         // { find style index for this run and retrieve corresponding style attributes }
  309.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  310.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  311.  
  312.         // { _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run: }
  313.         // { correct this anomaly (which is useful for other purposes, anyway) }
  314.         if (runInfo.runEnd > pWE->textLength) 
  315.         {
  316.             runInfo.runEnd = pWE->textLength;
  317.         }
  318.         
  319.         // { apply changes to existing style attributes as requested }
  320.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  321.  
  322.         // { recalculate font metrics, if necessary }
  323.         if ((mode & (weDoFont + weDoSize + weDoFace + weDoAddSize)) != 0) 
  324.         {
  325.             _WEFillFontInfo(pWE->port, &runInfo.runAttrs);
  326.         }
  327.         
  328.         // { get a style index for the new attributes }
  329.         err = _WENewStyle(&runInfo.runAttrs, &newStyleIndex, pWE);
  330.         if (err != noErr) 
  331.         {
  332.             goto cleanup;
  333.         }
  334.         
  335.         // { if offset falls on a style boundary and this style run has become identical }
  336.         // { to the previous one, merge the two runs together }
  337.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  338.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  339.         {
  340.             err = _WERemoveRun(runIndex, pWE);
  341.             if (err != noErr) 
  342.             {
  343.                 goto cleanup;
  344.             }
  345.             runIndex = runIndex - 1;
  346.         }
  347.  
  348.         // { style index changed? }
  349.         if (oldStyleIndex != newStyleIndex) 
  350.         {
  351.             // { if offset is in the middle of a style run, insert a new style run in the run array }
  352.             if (offset > runInfo.runStart) 
  353.             {
  354.                 err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE);
  355.                 if (err != noErr) 
  356.                 {
  357.                     goto cleanup;
  358.                 }
  359.                 runIndex = runIndex + 1;
  360.             }
  361.             else
  362.             {
  363.                 // { otherwise just change the styleIndex field of the current style run element }
  364.                 _WEChangeRun(runIndex, newStyleIndex, pWE);
  365.             }
  366.  
  367.             // { if specified range ends in the middle of a style run, insert yet another element }
  368.             if (rangeEnd < runInfo.runEnd) 
  369.             {
  370.                 err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE);
  371.                 if (err != noErr) 
  372.                 {
  373.                     goto cleanup;
  374.                 }
  375.             }
  376.         } // { if oldStyle != newStyle }
  377.  
  378.         // { go to next style run }
  379.         runIndex = runIndex + 1;
  380.         offset = runInfo.runEnd;
  381.  
  382.     } while (offset < rangeEnd);
  383.  
  384.     // { if the last style run ends exactly at the end of the specified range, }
  385.     // { see if we can merge it with the following style run }
  386.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) && 
  387.         ((*hRuns)[runIndex].styleIndex == newStyleIndex)) 
  388.     {
  389.         err = _WERemoveRun(runIndex, pWE);
  390.         if (err != noErr) 
  391.         {
  392.             goto cleanup;
  393.         }
  394.     }
  395.  
  396.     // { clear result code }
  397.     err = noErr;
  398.  
  399. cleanup:
  400.     // { return result code }
  401.     return err;
  402. }
  403.  
  404. OSErr _WEApplyStyleScrap(long rangeStart, long rangeEnd, WEStyleScrapHandle styleScrap, WEHandle hWE)
  405. {
  406.     // { apply the given style scrap to the specified text range }
  407.     // { the WE record is guaranteed to be already locked }
  408.  
  409.     WEPtr pWE;
  410.     WEStyleScrapPeek pElement;
  411.     long runStart, runEnd;
  412.     short index, lastElement;
  413.     WETextStyle ts;
  414.     OSErr err;
  415.  
  416.     // _WEApplyStyleScrap = noErr;
  417.     pWE = *hWE;
  418.  
  419.     // { loop through each element of the style scrap }
  420.     lastElement = (*styleScrap)->scrpNStyles - 1;
  421.     for(index = 0; index<=lastElement; index++)
  422.     {
  423.         // { get a pointer to the current scrap element }
  424.         pElement = (WEStyleScrapPeek)&(*styleScrap)->scrpStyleTab[index];
  425.  
  426.         // { calculate text run to which this element is to be applied }
  427.         runStart = rangeStart + pElement->first.scrpStartChar;
  428.         if (index < lastElement) 
  429.         {
  430.             runEnd = rangeStart + pElement->second.scrpStartChar;
  431.         }
  432.         else
  433.         {
  434.             runEnd = rangeEnd;
  435.         }
  436.         
  437.         // { perform some range checking }
  438.         if (runEnd > rangeEnd) 
  439.         {
  440.             runEnd = rangeEnd;
  441.         }
  442.         if (runStart >= runEnd) 
  443.         {
  444.             continue;
  445.         }
  446.  
  447.         // { copy style to a local variable in case memory moves }
  448.         ts = pElement->first.scrpAttrs.runStyle;
  449.  
  450.         // { apply the specified style to the range }
  451.         err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE);
  452.         if (err != noErr) 
  453.         {
  454.             return err;
  455.         }
  456.     }
  457.     return noErr;
  458. }
  459.  
  460. void _WEBumpRunStart(long runIndex, long deltaRunStart, WEPtr pWE)
  461. {
  462.     // { add deltaLineStart to the lineStart field of all line records }
  463.     // { starting from lineIndex }
  464.  
  465.     long *pStart;
  466.     long nRuns;
  467.  
  468.     pStart = &(*pWE->hRuns)[runIndex].runStart;
  469.     nRuns = pWE->nRuns;
  470.  
  471.     // { loop through the style run array adjusting the runStart fields }
  472.     while (runIndex <= nRuns)
  473.     {
  474.         *pStart = *pStart + deltaRunStart;
  475.         pStart = (long *)((long)(pStart) + sizeof(RunArrayElement));
  476.         runIndex = runIndex + 1;
  477.     }
  478. }
  479.  
  480. OSErr _WERemoveRunRange(long rangeStart, long rangeEnd, WEHandle hWE)
  481. {
  482.     // { the range of text between rangeStart and rangeEnd is being deleted }
  483.     // { update the style run array (and the style table) accordingly }
  484.     // { the WE handle must be locked on entry }
  485.  
  486.     WEPtr pWE;
  487.     RunArrayPeek pRuns;
  488.     long startRun, endRun;
  489.     OSErr err;
  490.  
  491.     pWE = *hWE;
  492.  
  493.     // { find the index to the first and last style runs in the specified range }
  494.     startRun = _WEOffsetToRun(rangeStart, hWE);
  495.     endRun = _WEOffsetToRun(rangeEnd, hWE) - 1;
  496.  
  497.     // { remove all style runs between startRun and endRun }
  498.     while (endRun > startRun)
  499.     {
  500.         err = _WERemoveRun(endRun, pWE);
  501.         if (err != noErr) 
  502.         {
  503.             goto cleanup;
  504.         }
  505.         endRun = endRun - 1;
  506.     }
  507.     
  508.     // { move back all subsequent style runs }
  509.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  510.  
  511.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  512.     { 
  513.         pRuns = (RunArrayPeek)&(*pWE->hRuns)[endRun];
  514.         pRuns->second.runStart = rangeStart;
  515.     }
  516.  
  517.     // { remove the first style run if is has become zero length }
  518.     pRuns = (RunArrayPeek)&(*pWE->hRuns)[startRun];
  519.     if (pRuns->first.runStart == pRuns->second.runStart) 
  520.     {
  521.         err = _WERemoveRun(startRun, pWE);
  522.         if (err != noErr) 
  523.         {
  524.             goto cleanup;
  525.         }
  526.         startRun = startRun - 1;
  527.     }
  528.  
  529.     // { merge the first and last runs if they have the same style index }
  530.     if (startRun >= 0) 
  531.     {
  532.         pRuns = (RunArrayPeek)&(*pWE->hRuns)[startRun];
  533.         if (pRuns->first.styleIndex == pRuns->second.styleIndex) 
  534.         {
  535.             err = _WERemoveRun(startRun + 1, pWE);
  536.             if (err != noErr) 
  537.             {
  538.                 goto cleanup;
  539.             }
  540.         }
  541.     }
  542.     // { clear result code }
  543.     err = noErr;
  544.  
  545. cleanup:
  546.     // { return result code }
  547.     return err;
  548. }
  549.  
  550. void _WEBumpLineStart(long lineIndex, long deltaLineStart, WEPtr pWE)
  551. {
  552.     // { add deltaLineStart to the lineStart field of all line records }
  553.     // { starting from lineIndex }
  554.  
  555.     long *pStart;
  556.     long nLines;
  557.  
  558.     pStart = &(*pWE->hLines)[lineIndex].lineStart;
  559.     nLines = pWE->nLines;
  560.  
  561.     // { loop through the line array adjusting the lineStart fields }
  562.     while (lineIndex <= nLines)
  563.     {
  564.         *pStart = *pStart + deltaLineStart;
  565.         pStart = (long *)((long)pStart + sizeof(LineRec));
  566.         lineIndex = lineIndex + 1;
  567.     }
  568. }
  569.  
  570. OSErr _WERemoveLineRange(long rangeStart, long rangeEnd, WEHandle hWE)
  571. {
  572.     // { the range of text between rangeStart and rangeEnd is being deleted }
  573.     // { update the line array accordingly }
  574.     // { the WE handle must be locked on entry }
  575.  
  576.     WEPtr pWE;
  577.     long startLine, endLine;
  578.     OSErr err;
  579.     
  580.     // _WERemoveLineRange = noErr;
  581.     pWE = *hWE;
  582.  
  583.     // { remove all line records between rangeStart and rangeEnd }
  584.     startLine = _WEOffsetToLine(rangeStart, hWE) + 1;
  585.     endLine = _WEOffsetToLine(rangeEnd, hWE);
  586.     while (endLine >= startLine)
  587.     {
  588.         err = _WERemoveLine(endLine, pWE);
  589.         if (err != noErr) 
  590.         {
  591.             return err;
  592.         }
  593.         endLine = endLine - 1;
  594.     } // { while }
  595.  
  596.     // { update the lineStart field of all the line records that follow }
  597.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  598.  
  599.     return noErr;
  600. }
  601.  
  602. OSErr _WEDeleteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  603. {
  604.     // { used internally to delete a text range }
  605.     // { if saveNullStyle is TRUE, the first style in the range is saved in nullStyle }
  606.     // { the WE record is guaranteed to be already locked }
  607.  
  608.     WEPtr pWE;
  609.     WERunInfo runInfo;
  610.     long oldTextLength, newTextLength;
  611.     long pText;
  612.     OSErr err;
  613.  
  614.     pWE = *hWE;
  615.  
  616.     // { do nothing if the specified range is empty }
  617.     if (rangeStart == rangeEnd) 
  618.     {
  619.         goto cleanup1;
  620.     }
  621.     
  622.     // { save the first style in the specified range in nullStyle }
  623.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  624.     pWE->nullStyle = runInfo.runAttrs;
  625.     BSET(pWE->flags, weFUseNullStyle);
  626.  
  627.     // { remove all line records between rangeStart and rangeEnd }
  628.     err = _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  629.     if (err != noErr) 
  630.     {
  631.         goto cleanup2;
  632.     }
  633.     
  634.     // { remove all style runs between rangeStart and rangeEnd }
  635.     err = _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  636.     if (err != noErr) 
  637.     {
  638.         goto cleanup2;
  639.     }
  640.     
  641.     // { calculate old and new text length }
  642.     oldTextLength = pWE->textLength;
  643.     newTextLength = oldTextLength - (rangeEnd - rangeStart);
  644.  
  645.     // { move the end of the text backwards over the old selection range }
  646.     pText = (long)(*pWE->hText);
  647.     BlockMoveData((Ptr)pText + rangeEnd, (Ptr)pText + rangeStart, oldTextLength - rangeEnd);
  648.  
  649.     // { compact the text handle }
  650.     SetHandleSize((Handle)pWE->hText, newTextLength);
  651.     err = MemError();
  652.     if (err != noErr) 
  653.     {
  654.         goto cleanup2;
  655.     }
  656.  
  657.     // { update textLength field }
  658.     pWE->textLength = newTextLength;
  659.  
  660. cleanup1:
  661.     // { clear result code }
  662.     err = noErr;
  663.  
  664. cleanup2:
  665.     // { return result code }
  666.     return err;
  667. }
  668.  
  669. OSErr _WEInsertText(long offset, Ptr textPtr, long textLength, WEHandle hWE)
  670. {
  671.     // { this routine assumes that the WE record is already locked }
  672.  
  673.     WEPtr pWE;
  674.     long oldTextLength, newTextLength;
  675.     long pInsPoint;
  676.     OSErr err;
  677.  
  678.     pWE = *hWE;
  679.  
  680.     // { do nothing if textLength is zero or negative }
  681.     if (textLength <= 0) 
  682.     {
  683.         goto cleanup1;
  684.     }
  685.     
  686.     // { calculate old and new length of text handle }
  687.     oldTextLength = pWE->textLength;
  688.     newTextLength = oldTextLength + textLength;
  689.  
  690.     // { leng the raw text handle }
  691.     SetHandleSize((Handle)pWE->hText, newTextLength);
  692.     err = MemError();
  693.     if (err != noErr) 
  694.     {
  695.         goto cleanup2;
  696.     }
  697.     
  698.     // { calculate ptr to insertion point }
  699.     pInsPoint = (long)(*pWE->hText) + offset;
  700.  
  701.     // { make room for the new text }
  702.     BlockMoveData((Ptr)pInsPoint, (Ptr)(pInsPoint + textLength), oldTextLength - offset);
  703.  
  704.     // { insert new text at the insertion point }
  705.     BlockMoveData(textPtr, (Ptr)pInsPoint, textLength);
  706.  
  707.     // { update the lineStart fields of all lines following the insertion point }
  708.     _WEBumpLineStart(_WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  709.  
  710.     // { update the runStart fields of all style runs following the insertion point }
  711.     _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  712.  
  713.     // { update various fields in the WE record }
  714.     pWE->textLength = newTextLength;
  715.  
  716.     // { if there is a valid null style, apply it to the newly inserted text }
  717.     if (BTST(pWE->flags, weFUseNullStyle)) 
  718.     {
  719.         err = _WESetStyleRange(offset, offset + textLength, weDoAll + weDoReplaceFace, &pWE->nullStyle.runStyle, hWE);
  720.         if (err != noErr)
  721.         {
  722.             goto cleanup2;
  723.         }
  724.     } 
  725.  
  726. cleanup1:
  727.     // { clear result code }
  728.     err = noErr;
  729.  
  730. cleanup2:
  731.     // { return result code }
  732.     return err;
  733. }
  734.  
  735. pascal OSErr WEDelete(WEHandle hWE)
  736. {
  737.     WEPtr pWE;
  738.     long offset;
  739.     // WERunInfo runInfo;
  740.     Boolean saveWELock;
  741.     OSErr err;
  742.     
  743.     // WEDelete = noErr;
  744.  
  745.     // { lock the WE record }
  746.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  747.     pWE = *hWE;
  748.     offset = pWE->selStart;
  749.  
  750.     // { do nothing if the selection range is empty }
  751.     if (offset < pWE->selEnd) 
  752.     {
  753.         // { delete the selection range }
  754.         err = _WEDeleteRange(offset, pWE->selEnd, hWE);
  755.         if (err != noErr) 
  756.         {
  757.             return err;
  758.         }
  759.         
  760.         // { selEnd becomes equal to selStart }
  761.         pWE->selEnd = offset;
  762.  
  763.         // { redraw the text }
  764.         err = _WERedraw(offset, offset, hWE);
  765.         if (err != noErr) 
  766.         {
  767.             return err;
  768.         }
  769.     } // { if non-empty selection }
  770.  
  771.     // { unlock the WE record }
  772.     _WESetHandleLock((Handle)hWE, saveWELock);
  773.  
  774.     return noErr;
  775. }
  776.  
  777. pascal OSErr WEInsert(Ptr textPtr, long textLength, WEStyleScrapHandle styleScrap, WEHandle hWE)
  778. {
  779.     WEPtr pWE;
  780.     long offset, endOffset;
  781.     Boolean saveWELock;
  782.     OSErr err;
  783.  
  784.     // { lock the WE record }
  785.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  786.     pWE = *hWE;
  787.     offset = pWE->selStart;
  788.  
  789.     // { delete current selection }
  790.     err = _WEDeleteRange(offset, pWE->selEnd, hWE);
  791.     if (err != noErr) 
  792.     {
  793.         goto cleanup;
  794.     }
  795.     
  796.     // { insert the new text at the insertion point }
  797.     err = _WEInsertText(offset, textPtr, textLength, hWE);
  798.     if (err != noErr) 
  799.     {
  800.         goto cleanup;
  801.     }
  802.     
  803.     // { move the insertion point at the end of the inserted text }
  804.     endOffset = offset + textLength;
  805.     pWE->selStart = endOffset;
  806.     pWE->selEnd = endOffset;
  807.  
  808.     if (styleScrap != nil) 
  809.     {
  810.         // { if a styleScrap was supplied, apply it to the newly inserted text }
  811.         err = _WEApplyStyleScrap(offset, endOffset, styleScrap, hWE);
  812.         if (err != noErr) 
  813.         {
  814.             goto cleanup;
  815.         }
  816.     }
  817.     
  818.     // { invalid the null style }
  819.     BCLR(pWE->flags, weFUseNullStyle);
  820.  
  821.     // { redraw the text }
  822.     err = _WERedraw(offset, endOffset, hWE);
  823.     if (err != noErr) 
  824.     {
  825.         goto cleanup;
  826.     }
  827.     
  828.     // { clear result code }
  829.     err = noErr;
  830.  
  831. cleanup:
  832.     // { unlock the WE record }
  833.     _WESetHandleLock((Handle)hWE, saveWELock);
  834.  
  835.     // { return result code }
  836.     return err;
  837. }
  838.  
  839. void _WEInsertByte(char theByte, WEHandle hWE)
  840. {
  841.     WEPtr pWE;
  842.     short db;
  843.     long charLength;
  844.     short byteType;
  845.     short saveFont;
  846.     GrafPtr savePort;
  847.  
  848.     pWE = *hWE;
  849.     charLength = 1;                // { assume 1-byte character by default }
  850.     db = theByte << 8;
  851.  
  852.     // { delete current selection, if any }
  853.     _WEDeleteRange(pWE->selStart, pWE->selEnd, hWE);
  854.     pWE->selEnd = pWE->selStart;
  855.  
  856.     // { make sure the font script is synchronized with the keyboard script }
  857.     _WESynchNullStyle(hWE);
  858.  
  859.     if (BTST(pWE->flags, weFDoubleByte)) 
  860.     {
  861.         // { special processing for double-byte characters }
  862.         if (pWE->firstByte != 0) 
  863.         {
  864.             // { if this byte is the second half of a double-byte character, }
  865.             // { insert the two bytes at the same time (flush the double-byte cache) }
  866.             db = pWE->firstByte << 8 + theByte;
  867.             charLength = 2;
  868.             pWE->firstByte = 0;
  869.         }
  870.         else
  871.         {
  872.             // { determine the byte-type of theByte; first set up the port and its font }
  873.             GetPort(&savePort);
  874.             SetPort(pWE->port);
  875.             saveFont = pWE->port->txFont;
  876.             TextFont(pWE->nullStyle.runStyle.tsFont);
  877.  
  878.             // { call CharByte }
  879.             byteType = CharByte(&theByte, 0);
  880.  
  881.             // { put back font and port }
  882.             TextFont(saveFont);
  883.             SetPort(savePort);
  884.  
  885.             // { if theByte is the first half of a double-byte character, just cache it and exit }
  886.             if (byteType == smFirstByte) 
  887.             {
  888.                 pWE->firstByte = theByte;
  889.                 return;
  890.             }
  891.         }
  892.     } // { if double-byte script installed }
  893.  
  894.     // { insert the new character into the text }
  895.     WEInsert((char *)&db, charLength, nil, hWE);
  896. }
  897.  
  898. void _WEBackspace(WEHandle hWE)
  899. {
  900.     // { this routine is called by WEKey to handle the backspace key }
  901.     // { the WE record is guaranteed to be already locked }
  902.  
  903.     WEPtr pWE;
  904.     long offset, charLength;
  905.     OSErr err;
  906.     
  907.     pWE = *hWE;
  908.     offset = pWE->selStart;
  909.  
  910.     // { if the selection range is not empty, delete the current selection range }
  911.     if (offset < pWE->selEnd) 
  912.     {
  913.             err = _WEDeleteRange(offset, pWE->selEnd, hWE);
  914.     }
  915.     else
  916.     {
  917.         // { do nothing if insertion point is at the beginning of the text }
  918.         if (offset <= 0) 
  919.         {
  920.             return;
  921.         }
  922.         
  923.         // { determine the byte-type of the character preceding the insertion point }
  924.         if (WECharByte(offset - 1, hWE) == smSingleByte) 
  925.         {
  926.             charLength = 1;
  927.         }
  928.         else
  929.         {
  930.             charLength = 2;
  931.         }
  932.  
  933.         // { delete the character }
  934.         offset = offset - charLength;
  935.         err = _WEDeleteRange(offset, offset + charLength, hWE);
  936.     }
  937.  
  938.     // { keep track of current selection range }
  939.     pWE->selStart = offset;
  940.     pWE->selEnd = offset;
  941.  
  942.     // { redraw the text }
  943.     err = _WERedraw(offset, offset, hWE);
  944. }
  945.  
  946. pascal void WEKey(short key, short modifiers, WEHandle hWE)
  947. {
  948.     WEPtr pWE;
  949.     Boolean saveWELock;
  950.  
  951.     // { lock the WE record }
  952.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  953.     pWE = *hWE;
  954.  
  955.     // { hide the caret if it's showing }
  956.     if (BTST(pWE->flags, weFCaretVisible))
  957.     {
  958.         _WEDrawCaret(hWE);
  959.     }
  960.     
  961.     // { hide the cursor (it will show again as soon as it's moved) }
  962.     ObscureCursor();
  963.  
  964.     // { dispatch on key class (arrow keys, printable characters, backspace) }
  965.     if ((key >= kArrowLeft) && (key <= kArrowDown)) 
  966.     {
  967.         _WEDoArrowKey(key, modifiers, hWE);
  968.     }
  969.     else if (key == kBackspace)
  970.     { 
  971.         _WEBackspace(hWE);
  972.     }
  973.     else
  974.     {
  975.         _WEInsertByte(key, hWE);
  976.     }
  977.     
  978.     // { unlock the WE record }
  979.     _WESetHandleLock((Handle)hWE, saveWELock);
  980. }
  981.  
  982. pascal OSErr WECut(WEHandle hWE)
  983. {
  984.     OSErr err;
  985.  
  986.     // WECut = noErr;
  987.  
  988.     // { Cut is just Copy + Delete }
  989.     err = WECopy(hWE);
  990.     if (err != noErr) 
  991.     {
  992.         return err;
  993.     }
  994.  
  995.     err = WEDelete(hWE);
  996.     if (err != noErr) 
  997.     {
  998.         return err;
  999.     }
  1000.     return noErr;
  1001. }
  1002.  
  1003. pascal OSErr WESetStyle(short mode, WETextStyle *ts, WEHandle hWE)
  1004. {
  1005.     WEPtr pWE;
  1006.     ScriptCode fontScript;
  1007.     Boolean saveWELock;
  1008.     OSErr err;
  1009.  
  1010.     // { lock the WE record }
  1011.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1012.     pWE = *hWE;
  1013.  
  1014.     // { if the selection range is empty, set the null style }
  1015.     if (pWE->selStart == pWE->selEnd) 
  1016.     {
  1017.         // { first make sure the nullStyle field contains valid information }
  1018.         _WESynchNullStyle(hWE);
  1019.  
  1020.         // { apply style changes to the nullStyle record }
  1021.         _WECopyStyle(ts, &pWE->nullStyle.runStyle, pWE->nullStyle.runStyle.tsFace, mode);
  1022.  
  1023.         // { if the font was altered, synchronize the keyboard script }
  1024.         if (BTST(pWE->flags, weFNonRoman))
  1025.         {
  1026.             if (BTST(mode, kModeFont))
  1027.             {
  1028.                 fontScript = Font2Script(pWE->nullStyle.runStyle.tsFont);
  1029.                 if (fontScript != GetEnvirons(smKeyScript)) 
  1030.                 {
  1031.                     KeyScript(fontScript);
  1032.                 }
  1033.             }
  1034.         }
  1035.     }
  1036.     else
  1037.     {
  1038.         // { otherwise set the style of the selection range }
  1039.         err = _WESetStyleRange(pWE->selStart, pWE->selEnd, mode, ts, hWE);
  1040.         if (err != noErr)
  1041.         {
  1042.             goto cleanup;
  1043.         }
  1044.  
  1045.         // { and redraw the text }
  1046.         err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1047.         if (err != noErr)
  1048.         {
  1049.             goto cleanup;
  1050.         }
  1051.         
  1052.     }
  1053.  
  1054.     // { clear the result code }
  1055.     err = noErr;
  1056.  
  1057. cleanup:
  1058.     // { unlock the WE record }
  1059.     _WESetHandleLock((Handle)hWE, saveWELock);
  1060.  
  1061.     // { return result code }
  1062.     return err;
  1063. }
  1064.  
  1065. pascal OSErr WEUseStyleScrap(WEStyleScrapHandle styleScrap, WEHandle hWE)
  1066. {
  1067.     WEPtr pWE;
  1068.     Boolean saveWELock;
  1069.     OSErr err;
  1070.  
  1071.     // { lock the WE record }
  1072.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1073.     pWE = *hWE;
  1074.  
  1075.     // { apply the style scrap to the selection range }
  1076.     err = _WEApplyStyleScrap(pWE->selStart, pWE->selEnd, styleScrap, hWE);
  1077.     if (err != noErr) 
  1078.     {
  1079.         goto cleanup;
  1080.     }
  1081.  
  1082.     // { redraw the text }
  1083.     err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1084.  
  1085. cleanup:
  1086.     // { unlock the WE record }
  1087.     _WESetHandleLock((Handle)hWE, saveWELock);
  1088.  
  1089.     // { return result code }
  1090.     return err;
  1091. }
  1092.  
  1093. pascal OSErr WEPaste(WEHandle hWE)
  1094. {
  1095.     Handle hText, hStyles;
  1096.     long scrapResult, scrapOffset;
  1097.     OSErr err;
  1098.  
  1099.     hText = nil;
  1100.     hStyles = nil;
  1101.  
  1102.     // { allocate a handle to hold the text }
  1103.     err = _WEAllocate(0, kAllocTemp, (Handle *)&hText);
  1104.     if (err != noErr) 
  1105.     {
  1106.         goto cleanup;
  1107.     }
  1108.     
  1109.     // { look for a 'TEXT' item }
  1110.     scrapResult = GetScrap(hText, kTypeText, &scrapOffset);
  1111.     if (scrapResult <= 0) 
  1112.     {
  1113.         err = scrapResult;
  1114.         goto cleanup;
  1115.     }
  1116.     
  1117.     // { allocate a handle to hold the style scrap }
  1118.     err = _WEAllocate(0, kAllocTemp, (Handle *)&hStyles);
  1119.     if (err != noErr) 
  1120.     {
  1121.         goto cleanup;
  1122.     }
  1123.     
  1124.     // { look for a 'styl' item accompanying the text }
  1125.     scrapResult = GetScrap(hStyles, kTypeStyles, &scrapOffset);
  1126.  
  1127.     // { forget the handle if nothing was found or an error occurred }
  1128.     if (scrapResult <= 0) 
  1129.     {
  1130.         _WEForgetHandle((Handle *)&hStyles);
  1131.     }
  1132.     
  1133.     // { lock down the text }
  1134.     HLock(hText);
  1135.  
  1136.     // { insert the text }
  1137.     err = WEInsert(*hText, GetHandleSize(hText), (WEStyleScrapHandle)hStyles, hWE);
  1138.  
  1139. cleanup:
  1140.     // { clean up }
  1141.     _WEForgetHandle((Handle *)&hText);
  1142.     _WEForgetHandle((Handle *)&hStyles);
  1143.  
  1144.     // { return result code }
  1145.     return err;
  1146. }
  1147.